<?php
namespace Matrix;

/**
 * 视图类
 *
 * 用来构建视图
 *
 * 创建一个视图实例
 * <code>
 * View::make('admin::help.one', array('name1' => 'value1'));
 * // 或者
 * View::name('test', 'admin::help.one');
 * View::of('test')->with('name1', 'value1');
 * </code>
 */
class View implements \ArrayAccess
{
	/**
	 * 视图导入事件名
	 *
	 * @var string
	 */
	const loader = 'matrix.view.loader';
	
	/**
	 * 视图引擎事件名
	 *
	 * @var string
	 */
	const engine = 'matrix.view.engine';
	
	/**
	 * 视图名称
	 *
	 * @var string
	 */
	public $view;
	
	/**
	 * 视图数据
	 *
	 * @var array
	 */
	public $data = array();
	
	/**
	 * 视图路径
	 *
	 * @var string
	 */
	public $path;
	
	/**
	 * 所有共享的视图数据
	 *
	 * @var array
	 */
	public static $shared = array();
	
	/**
	 * 所有注册的视图名称
	 *
	 * @var array
	 */
	public static $names = array();
	
	/**
	 * 所有导入视图文件的内容缓存
	 *
	 * @var array
	 */
	public static $cache = array();
	
	/**
	 * 创建一个视图实例
	 *
	 * @param	stirng	$view
	 * @param	array	$data
	 * @return	void
	 */
	public function __construct($view, $data = array())
	{
		$this->view = $view;
		$this->data = $data;
		// 如果视图以path: 开头, 证明视图路径是个绝对路径
		if (starts_with($view, 'path: ')) {
			$this->path = substr($view, 6);
		} else {
			$this->path = $this->path($view);
		}
		// 如果session驱动被指定, 并且存在错误, 将绑定一个验证错误消息在视图
		if (!isset($this->data['errors'])) {
			if (Session::started() and Session::has('errors')) {
				$this->data['errors'] = Session::get('errors');
			} else {
				$this->data['errors'] = new Messages;
			}
		}
	}
	
	/**
	 * 循环一个视图部分, 返回得到渲染内容
	 *
	 * @param	string	$view 
	 * @param	array	$data
	 * @param	string	$iterator
	 * @param	string	$empty
	 * @return	string
	 */
	public function render_each($view, array $data, $iterator, $empty = 'raw|')
	{
		$result = '';
		if (!empty($data)) {
			foreach ($data as $key => $value) {
				$with = array('key' => $key, $iterator => $value);
				$result .= static::make($view, $with)->render();
			}
		} else {
			if (starts_with($empty, 'raw|')) {
				$result = substr($empty, 4);
			} else {
				$result = static::make($view)->render();
			}
		}
	}
	
	/**
	 * 获取视图内容
	 *
	 * @return	string
	 */
	public function render()
	{
		Event::fire("laravel.composing: {$this->view}", array($this));
		if (Event::listeners(static::engine)) {
			$result = Event::until(static::engine, array($this));
			if ($result !== null) {
				return $result;
			}
		}
		return $this->get();
	}
	
	/**
	 * 获取视图内容
	 *
	 * @return	string
	 */
	public function get()
	{
		$__data = $this->data();
		$__contents = $this->load();
		ob_start() and extract($__data, EXTR_SKIP);
		try {
			eval('?>'.$__contents);
		} catch (\Exception $e) {
			// 如果捕获到异常, 清空缓存, 抛出异常
			ob_get_clean(); throw $e;
		}
		return ob_get_clean();
	}
	
	/**
	 * 检测一个视图是否存在
	 *
	 * @param	string	$view
	 * @param	boolean	$return_path
	 * @return	boolean
	 */
	public static function has($view, $return_path = false)
	{
		list($bundle, $view) = Bundle::parse($view);
		$view = str_replace('.', '/', $view);
		$path = Event::until(static::loader, array($bundle, $view));
		if ($path !== null) {
			return $return_path ? $path : true;
		}
		return false;
	}
	
	/**
	 * 添加一个键值对, 在所有视图直接共享
	 *
	 * @param	string	$key
	 * @param	mixed	$value
	 * @return	View
	 */
	public function shares($key, $value)
	{
		static::share($key, $value);
		return $this;
	}
	
	/**
	 * 添加一个键值对, 只有当前视图可用
	 *
	 * @param	string	$key
	 * @param	mixed	$value
	 * @return	View
	 */
	public function with($key, $value = null)
	{
		if (is_array($key)) {
			$this->data = array_merge($this->data, $key);
		} else {
			$this->data[$key] = $value;
		}
		return $this;
	}
	
	/**
	 * 添加一个视图内容到数据键
	 *
	 * @param	string	$key
	 * @param	string	$view
	 * @param	mixed	$value
	 * @return	View
	 */
	public function nest($key, $view, $data = array())
	{
		return $this->with($key, static::make($view, $data));
	}
	
	/**
	 * 创建一个视图实例
	 *
	 * @param	stirng	$view
	 * @param	array	$data
	 * @return	void
	 */
	public static function make($view, $data = array())
	{
		return new static($view, $data);
	}
	
	/**
	 * 创建一个别名视图实例
	 *
	 * @param	stirng	$view
	 * @param	array	$data
	 * @return	void
	 */
	public static function of($name, $data = array())
	{
		return new static(static::$names[$name], $data);
	}
	
	/**
	 * 赋值一个名到一个视图, 相当于别名
	 *
	 * @param	string	$name
	 * @param	string	$view
	 * @return	void
	 */
	public static function name($name, $view)
	{
		static::$names[$name] = $view;
	}
	
	/**
	 * 注册一个视图编辑器, 通过添加监听事件实现
	 *
	 * @param	string	$views
	 * @param	Closure	$composer
	 * @return	void
	 */
	public static function composer($views, \Closure $composer)
	{
		$views = (array)$views;
		foreach ($views as $view) {
			Event::listen("laravel.composing: {$view}", $composer);
		}
	}
	
	/**
	 * 获取视图文件地址, 用于matrix.view.loader事件
	 *
	 * @param	string	$bundle
	 * @param	string	$view
	 * @param	string	$directory
	 * @return	mixed
	 */
	public static function file($bundle, $view, $directory)
	{
		$directory = str_finish($directory, DS);
		// 非刀锋模板引擎优先级高于刀锋模板引擎视图文件
		if (file_exists($path = $directory.$view.EXT) || file_exists($path = $directory.$view.BLADE_EXT)) {
			return $path;
		}
	}
	
	/**
	 * 添加一个键值对, 在所有视图直接共享
	 *
	 * @param	string	$key
	 * @param	mixed	$value
	 * @return	void
	 */
	public static function share($key, $value)
	{
		static::$shared[$key] = $value;
	}
	
	/**
	 * PHP魔术方法, 支持$view->key访问数据
	 *
	 * @param	string	$key
	 * @return	mixed
	 */
	public function __get($key)
	{
		if (isset($this->data[$key])) {
			return $this->data[$key];
		}
	}
	
	/**
	 * PHP魔术方法, 支持$view->key = 'value'设置数据
	 *
	 * @param	string	$key
	 * @param	mixed	$value
	 * @return	void
	 */
	public function __set($key, $value)
	{
		$this->data[$key] = $value;
	}
	
	/**
	 * PHP魔术方法, 支持isset($view->key)判断数据是否存在
	 *
	 * @param	string	$key
	 * @return	boolean
	 */
	public function __isset($key)
	{
		return isset($this->data[$key]);
	}
	
	/**
	 * PHP魔术方法, 支持unset($view->key)注销数据
	 *
	 * @param	string	$key
	 * @return	void
	 */
	public function __unset($key)
	{
		unset($this->data[$key]);
	}
	
	/**
	 * PHP魔术方法, 支持echo $view访问视图内容
	 *
	 * @return	string
	 */
	public function __toString()
	{
		return $this->render();
	}
	
	/**
	 * 获取视图路径
	 *
	 * @param	string	$view
	 * @return	string
	 * @throws	Exception
	 */
	protected function path($view)
	{
		if ($path = $this->has($view,true)) {
			return $path;
		}
		throw new \Exception("View [$view] doesn't exist.");
	}
	
	/**
	 * 获取视图数据
	 *
	 * @return	array
	 */
	protected function data()
	{
		$data = array_merge($this->data, static::$shared);
		foreach ($data as $key => $value) {
			if ($value instanceof View || $value instanceof Response) {
				$data[$key] = $value->render();
			}
		}
		return $data;
	}
	
	/**
	 * 从硬盘获取视图内容
	 *
	 * @return	string
	 */
	protected function load()
	{
		if (isset(static::$cache[$this->path])) {
			return static::$cache[$this->path];
		}
		return static::$cache[$this->path] = file_get_contents($this->path);
	}
	
	/**
	 * 实现ArrayAccess接口
	 *
	 */
	public function offsetExists($offset)
	{
		return isset($this->data[$offset]);
	}
	
	public function offsetGet($offset)
	{
		if (isset($this->data[$offset])) {
			return $this->data[$offset];
		}
	}
	
	public function offsetSet($offset, $value)
	{
		$this->data[$offset] = $data;
	}
	
	public function offsetUnset($offset)
	{
		unset($this->data[$offset]);
	}
	
}